// ServerSocket.cpp

// Implementacia komunikacie so soccer serverom prostrednictvom UDP

#include "Common/Protocol/ServerSocket.h"
#include "Common/Tasks.h"
#include "Common/Support/Debug.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#ifdef WIN32
	#include <winsock.h>
#else	// WIN32
	#include <sys/types.h>
	#include <sys/time.h>
	#include <sys/socket.h>
	#include <netinet/in.h>
	#include <unistd.h>
	#include <netdb.h>
	#include <arpa/inet.h>
#endif	// WIN32

// Vyprsanie casoveho spojenia v sekundach (Connection timeout)
#define TIMEOUT 10

// Definicia konstruktora
ServerSocket::ServerSocket() : Module("ServerSocket", "Modul ServerSocket", "v0.15"),  parser(NULL), connected(false), sock(0)
{
#ifdef WIN32
	WORD ver = MAKEWORD(1, 1);
	WSADATA wsaData;
	// Inicializovanie Winsock
	if(WSAStartup(ver, &wsaData) != 0) 
		perror("WSAStartup failed");
#endif	// WIN32

	sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);

#ifdef WIN32
	unsigned long tmp = 0;
	ioctlsocket(sock, FIONBIO, &tmp);
#endif	// WIN32*/

	sockaddr_in cli_addr;
	cli_addr.sin_family = AF_INET;
	cli_addr.sin_port = htons(0);
	cli_addr.sin_addr.s_addr = htonl(INADDR_ANY);
	// Spaja miestnu adresu so socketom
	bind(sock, (sockaddr*) &cli_addr, sizeof(cli_addr));

	memset(&address, 0, sizeof(address));
	address.sin_family = AF_INET;

	Main::GetInstance()->RegisterModule(this, COMMUNICATION_TASK);
}

ServerSocket::~ServerSocket()
{
#ifdef WIN32
	closesocket(sock);
	WSACleanup();
#else	// WIN32
	close(sock);
#endif	// WIN32
}

int ServerSocket::Connect(const char* msg)
{
	connected = true;
	return SendMsg(msg);
}

int ServerSocket::Disconnect(const char* msg)
{
	if(SendMsg(msg))
		return 1;
	connected = false;

	return 0;
}

int ServerSocket::Init(int argc, char** argv, int)
{
	parser = (MsgProcessInterface*) Main::GetInstance()->GetInterface(MESSAGE_PROCESS_TASK);
	if(parser == NULL)
		return INIT_ERR;
    
//	clangparser = (MsgProcessInterface*) Main::GetInstance()->GetInterface(CLANG_MESSAGE_PROCESS_TASK);
//	if(clangparser == NULL) return INIT_ERR;

	return INIT_OK;
}

int ServerSocket::ReceiveMsg()
{
	if(!connected)
	{
	// Chyba, nepripojeny!
#ifdef WIN32
		//_sleep(100);
		Sleep(100);
#else	// WIN32
		usleep(100000);
#endif	// WIN32
		return 1;
	}

#ifdef WIN32
	int fromlen = sizeof(address);
#else	// WIN32
	socklen_t fromlen = sizeof(address);
#endif	// WIN32

	struct timeval timeout;
	timeout.tv_sec = TIMEOUT;
	timeout.tv_usec = 0;

	fd_set sockset; FD_ZERO(&sockset); FD_SET(sock, &sockset);
	
	if(select(sock + 1, &sockset, NULL, NULL, &timeout) < 0)
	{
		message[0] = 0;
		return 1;				// chyba
	}

	if (!FD_ISSET(sock, &sockset))
	{
		message[0] = 0;
		return 1;				// chyba, timeout
	}

	int rval = recvfrom(sock, message, BUFFERSIZE - 1, 0, (sockaddr*) &address, &fromlen);
	
	if(rval <= 0)
	{
		// Chyba pri prijimani
		message[0] = 0;
		return 1;				// chyba
	}

	// Prijatie uspesne
	message[rval] = 0;	// dam na koniec 0 (ukoncenie retazca)

	//DBGN("RECV: %s", message);

	// Parsujeme spravu
	if(parser->ParseMsg(message) != 0)
	{
        // GP
//        if(clangparser->ParseMsg(message) != 0)
 		  WARNN("Nemohol parsovat serverovu spravu %s", message);
	}

	return 0;
}

void ServerSocket::Run(int)
{
	int status = ReceiveMsg();
	// Test, ci spadol server
	if(status && connected)
	{
		ERRN("Server spadol!");
		Disconnect("(bye)");
		Main::GetInstance()->ShutDown();
	}
}

int ServerSocket::SendMsg(const char* msg)
{
	if(!connected)
		return 1;	// chyba, nepripojeny!

	int rval = sendto(sock, msg, strlen(msg) + 1, 0, (sockaddr*) &address, sizeof(address));
	if(rval <= 0)
		return 1;		// chyba

//	DBGN("SEND: %s", msg);

	return 0;
}

int ServerSocket::SetServer(const char* addr, unsigned port)
{
	struct hostent* host = gethostbyname(addr);

	if(host == NULL)
	{
		if(inet_addr(addr) == INADDR_NONE)
			return 1;	// chyba
	}
	else
	{
		addr = inet_ntoa(*(struct in_addr*) *host->h_addr_list);
	}

	address.sin_port = htons(port);
	address.sin_addr.s_addr = inet_addr(addr);

	return 0;
}

Interface* ServerSocket::GetInterface(int nTask) const
{
	return (CommunicationInterface*) this;
}

